#include "ParticleSystem.h"

//PARTICLE SYSTEM

ParticleSystem::ParticleSystem(IDirect3DDevice9* InitialDevice, const std::string EffectFileName, const std::string techniqueName, D3DXVECTOR3 nPosition, int MaximumParticles, float nTimePerParticle)
{
	Position = nPosition;
	D3DVERTEXELEMENT9 tempdecl[] = 
	{
		{0, 0 , D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
		{0, 24, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR   , 0},
		{0, 40, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2},
		{0, 44, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3},
		{0, 48, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4},
		D3DDECL_END()

	};
	InitialDevice->CreateVertexDeclaration(tempdecl, &decl);
	EffectDevice = InitialDevice;
	MaxParticles = MaximumParticles;
	TimePerParticle = nTimePerParticle;

	srand(unsigned int(clock()));

	ID3DXBuffer *errors = NULL;

	D3DXCreateEffectFromFile(EffectDevice, EffectFileName.c_str(), NULL, NULL, D3DXSHADER_DEBUG, 0, &Effect, &errors);

	if(errors != NULL)
	{
		MessageBox(NULL,
			EffectFileName.c_str(),
			"D3DXCreateEffectFromFile",
			MB_ICONERROR);

#ifdef _DEBUG
		MessageBox(NULL,
			(char*)errors->GetBufferPointer(),
			"D3DXCreateEffectFromFile",
			MB_ICONERROR);
#endif

		DebugBreak();
	}

	Technique = Effect->GetTechniqueByName(techniqueName.c_str());
	WorldViewProjection = Effect->GetParameterByName(0, "gWVP");
	ViewportHeight = Effect->GetParameterByName(0, "gViewportHeight");
	EyeHandle = Effect->GetParameterByName(0, "gEyePosW");

	Time = 0;

	Particles.resize(MaxParticles);
	DeadParticles.reserve(MaxParticles);
	AliveParticles.reserve(MaxParticles);

	for(int i = 0; i < MaxParticles; i++)
	{
		Particles[i].Color = D3DCOLOR(D3DCOLOR_ARGB(255, 255, 255, 255));
		Particles[i].Position = D3DXVECTOR3(0.0, 0.0, 0.0);
		Particles[i].StartTime = 0;
		Particles[i].LifeTime = -1;
		Particles[i].Velocity = D3DXVECTOR3(0.0, 0.0, 0.0);
	}

	HR(EffectDevice->CreateVertexBuffer(MaxParticles*sizeof(Particle), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY | D3DUSAGE_POINTS,
		0, D3DPOOL_DEFAULT, &VertexBuffer, 0));
}

ParticleSystem::ParticleSystem()
{

}

ParticleSystem::~ParticleSystem()
{
	if(Effect != NULL)
	{
		Effect->Release();
		Effect = NULL;
	}
}

void ParticleSystem::SetWVPMatrix(const D3DXMATRIX &nWVP)
{
	WVPMat = nWVP;
}

void ParticleSystem::SetCameraPosition(const D3DXVECTOR3 &nCameraPositon)
{
	CameraPosition = nCameraPositon;
}

void ParticleSystem::SetPosition(D3DXVECTOR3 nPosition)
{
	Position = nPosition;
}

void ParticleSystem::Update(float dt)
{
	Time += dt;

	DeadParticles.resize(0);
	AliveParticles.resize(0);

	for(int i = 0; i < MaxParticles; i++)
	{
		if(Time-Particles[i].StartTime > Particles[i].LifeTime)
			DeadParticles.push_back(&Particles[i]);
		else
			AliveParticles.push_back(&Particles[i]);
	}
	if(TimePerParticle > 0.0f)
	{
		static float TimeAccum = 0.0f;
		TimeAccum += dt;
		while(TimeAccum >= TimePerParticle)
		{
			AddParticle();
			TimeAccum -= TimePerParticle;
		}
	}

	for(unsigned int i = 0; i < AliveParticles.size();++i)
	{
		AliveParticles[i]->Position += AliveParticles[i]->Velocity * dt;
	}

} 

void ParticleSystem::Render( )
{
	Effect->SetValue(EyeHandle, CameraPosition, sizeof(D3DXVECTOR3));
	//D3DXVECTOR3 eyePositionLocal;

	//D3DXVec3TransformCoord(&eyePositionLocal, &eyePositionWorld, &InverseWorldMat);

	HWND hwnd = gd3dApp->getMainWnd();

	RECT clientRect;
	GetClientRect(hwnd, &clientRect);

	HR(Effect->SetInt(ViewportHeight, clientRect.bottom));
	HR(Effect->SetMatrix(WorldViewProjection, &(WVPMat)));

	HR(Effect->SetTechnique(Technique));

	UINT numPasses = 0;
	HR(Effect->Begin(&numPasses, 0));
	HR(Effect->BeginPass(0));

	HR(EffectDevice->SetStreamSource(0, VertexBuffer, 0, sizeof(Particle)));
	HR(EffectDevice->SetVertexDeclaration(decl));

	Particle *p = NULL;
	HR(VertexBuffer->Lock(0,0,(void**)&p, D3DLOCK_DISCARD));
	int vbIndex = 0;

	for(unsigned int i = 0; i < AliveParticles.size(); ++i)
	{
		p[vbIndex] = *AliveParticles[i];
		++vbIndex;
	}

	HR(VertexBuffer->Unlock());
	if(vbIndex > 0)
	{
		HR(EffectDevice->DrawPrimitive(D3DPT_POINTLIST, 0 ,vbIndex));
	}
	HR(Effect->EndPass());
	HR(Effect->End());
}

void ParticleSystem::SetTime( float nTime )
{
	Time = nTime;
}

float ParticleSystem::GetTime( void )
{
	return Time;
}

void ParticleSystem::SetWorldMatrix(const D3DXMATRIX &world)
{
	WorldMat = world;
	D3DXMatrixInverse(&InverseWorldMat, NULL, &WorldMat);
}

void ParticleSystem::AddParticle( void )
{
	if(DeadParticles.size() > 0)
	{
		Particle *p = DeadParticles.back();
		initParticle(*p);

		DeadParticles.pop_back();
		AliveParticles.push_back(p);
	}
}

void ParticleSystem::onLostDevice( void )
{
	HR(Effect->OnLostDevice());
	ReleaseCOM(VertexBuffer);
}

void ParticleSystem::onResetDevice( void )
{
	HR(Effect->OnResetDevice())

		if(VertexBuffer)
		{
			HR(EffectDevice->CreateVertexBuffer(MaxParticles*sizeof(Particle), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY | D3DUSAGE_POINTS,
				0, D3DPOOL_DEFAULT, &VertexBuffer, 0));
		}
}

//END PARTICLE SYSTEM

MissileTrail::MissileTrail()
{

}

MissileTrail::MissileTrail(IDirect3DDevice9* InitialDevice, const std::string EffectFileName,const std::string techniqueName,
	D3DXVECTOR3 nPosition, int MaximumParticles, float nTimePerParticle)
	: ParticleSystem(InitialDevice, EffectFileName, techniqueName,
	nPosition, MaximumParticles, nTimePerParticle)
{
	srand(unsigned int(clock()));
	Angles = D3DXVECTOR3(0,0,0);
	IsActive = false;
	OldActive = false;
	SpawnVector = D3DXVECTOR3(0,0,1);
}

MissileTrail::~MissileTrail()
{

}

void MissileTrail::Update( float dt )
{
	D3DXVECTOR4 tempVector;

	D3DXMatrixRotationYawPitchRoll(&RotationMatrix, Angles.x, Angles.y, Angles.z);
	D3DXVec3Transform(&tempVector, &SpawnVector, &RotationMatrix);
	SpawnVector = D3DXVECTOR3(Position.x + tempVector.x, Position.y + tempVector.y, Position.z + tempVector.z);

	ParticleSystem::Update(dt);
}

void MissileTrail::Render( void )
{
	ParticleSystem::Render();
}

void MissileTrail::initParticle( Particle &out )
{
	if(IsActive)
	{
		out.StartTime = Time;
		out.LifeTime = float(rand()%100)/100;
		//out.Position = D3DXVECTOR3(this->Position.x + float(rand()%2)/10, this->Position.y + float(rand()%2)/10, this->Position.z + (float(rand()%2)-1));
		out.Position = this->Position;


		//D3DXVECTOR3 tempVec = D3DXVECTOR3( -float(rand()%3-1), -float(rand()%3-1), out.Position.z + (-float(rand()%10+5)));
		D3DXVECTOR3 tempVec = D3DXVECTOR3( float(rand()%3-1), float(rand()%3-1), -15.0f);

		D3DXVec3TransformCoord(&tempVec,&tempVec, &RotationMatrix);

		out.Velocity = tempVec;
		out.Color = D3DXCOLOR(255.0f, float(rand()%75)/100, float(rand()%75)/100, 255);
	}
}

void MissileTrail::Activate( void )
{
	IsActive = true;
	OldActive = true;
	TimePerParticle = .01f;
}

void MissileTrail::Deactivate( void )
{
	IsActive = false;
	TimePerParticle = -1;
}

bool MissileTrail::GetIsActive( void )
{
	return IsActive;
}

void MissileTrail::SetAngles( D3DXVECTOR3 nAngles)
{
	Angles = nAngles;
}

bool MissileTrail::GetOldActive( void )
{
	return OldActive;
}

void MissileTrail::Reset( void )
{
	IsActive = false;
	OldActive = false;
	Deactivate();
}

//EXPLOSION EFFECT

Explosion::Explosion() : ParticleSystem()
{
	IsActive = false;
};

Explosion::Explosion(IDirect3DDevice9* InitialDevice, const std::string EffectFileName,const std::string techniqueName,
		D3DXVECTOR3 nPosition, int MaximumParticles, float nTimePerParticle)
		: ParticleSystem(InitialDevice, EffectFileName, techniqueName,
		  nPosition, MaximumParticles, nTimePerParticle)
{
	IsActive = false;
	TimePerParticle = -1.f;
	RenewExplosion = true;
	ParticleLifeTime = 999999.f;
	gTime = Effect->GetParameterByName(0, "gTime");
}

void Explosion::Activate( void )
{
	Time = 0;
	IsActive = true;
	TimePerParticle = .0001f;
	ParticleLifeTime = 10.f;
}

void Explosion::Deactivate( void )
{
	IsActive = false;
	TimePerParticle = -1;
	ParticleLifeTime = -1.f;
}

void Explosion::initParticle( Particle &out )
{
	if(IsActive)
	{
		out.StartTime = Time;
		out.LifeTime = 10.f;
		//out.Position = D3DXVECTOR3(this->Position.x + float(rand()%2)/10, this->Position.y + float(rand()%2)/10, this->Position.z + (float(rand()%2)-1));
		out.Position = this->Position;


		//D3DXVECTOR3 tempVec = D3DXVECTOR3( -float(rand()%3-1), -float(rand()%3-1), out.Position.z + (-float(rand()%10+5)));
		D3DXVECTOR3 tempVec = D3DXVECTOR3( (float(rand()%2600-1300)/100) + (float(rand()%2600-1300)/100),
										   (float(rand()%2600-1300)/100) + (float(rand()%2600-1300)/100),
										   (float(rand()%2600-1300)/100) + (float(rand()%2600-1300)/100));

		out.Velocity = tempVec;
		float TempRand = float(rand()%75)/100;
		out.Color = D3DXCOLOR(255.0f, TempRand, float(rand()%75)/100, 255);
	}
}

void Explosion::Update(float dt)
{
	Time += dt;
	if(Time <= ParticleLifeTime)
	{
		DeadParticles.resize(0);
		AliveParticles.resize(0);

		for(int i = 0; i < MaxParticles; i++)
		{
			if(IsActive)
			{
				if(Time-Particles[i].StartTime > Particles[i].LifeTime)
				{
					DeadParticles.push_back(&Particles[i]);
				}
				else
				{
					AliveParticles.push_back(&Particles[i]);
				}
			}/*
			 else
			 {
			 Particles[i].Position = Position;
			 DeadParticles.push_back(&Particles[i]);
			 }*/
		}

		if(TimePerParticle > 0.0f )
		{
			static float TimeAccum = 0.0f;
			TimeAccum += dt;
			while(TimeAccum >= TimePerParticle)
			{
				AddParticle();
				TimeAccum -= TimePerParticle;
			}
		}
	}

	for(unsigned int i = 0; i < AliveParticles.size();++i)
	{
		AliveParticles[i]->Position += AliveParticles[i]->Velocity * dt;
	}
	/*if(Time >= ParticleLifeTime)
	{
	int i = 0;
	}*/

}

void Explosion::Render(void)
{
	Effect->SetFloat(gTime, Time);
	if(IsActive)
		ParticleSystem::Render();
}

bool Explosion::GetIsActive( void )
{
	return IsActive;
}

void Explosion::Reset( void )
{
	IsActive = false;
	RenewExplosion = true;
	Deactivate();

	for(unsigned int i = 0; i < AliveParticles.size(); i++)
	{
		AliveParticles[i]->Position = D3DXVECTOR3(0,0,0);
		AliveParticles[i]->LifeTime = -1;
		AliveParticles[i]->Velocity = D3DXVECTOR3(0,0,0);
		DeadParticles.push_back(AliveParticles[i]);
	}
	AliveParticles.clear();
}


//THRUSTER EFFECT


ThrusterEffect::ThrusterEffect()
{

}

ThrusterEffect::ThrusterEffect(IDirect3DDevice9* InitialDevice, const std::string EffectFileName,const std::string techniqueName,
	D3DXVECTOR3 nPosition, int MaximumParticles, float nTimePerParticle)
	: ParticleSystem(InitialDevice, EffectFileName, techniqueName,
	nPosition, MaximumParticles, nTimePerParticle)
{
	srand(unsigned int(clock()));
	Angles = D3DXVECTOR3(0,0,0);
	IsActive = false;
	SpawnVector = D3DXVECTOR3(0,0,1);
	SpawnVelocity = D3DXVECTOR3(0,0,0);
	Throttle = 0;
}

ThrusterEffect::~ThrusterEffect()
{

}

void ThrusterEffect::Update( float dt )
{
	D3DXVECTOR4 tempVector;

	if(Throttle != 0)
		TimePerParticle = float((1/(float(abs(Throttle))/100.f)) * .01); 

	D3DXMatrixRotationYawPitchRoll(&RotationMatrix, Angles.x, Angles.y, Angles.z);
	D3DXVec3Transform(&tempVector, &SpawnVector, &RotationMatrix);
	SpawnVector = D3DXVECTOR3(Position.x + tempVector.x, Position.y + tempVector.y, Position.z + tempVector.z);

	ParticleSystem::Update(dt);
}

void ThrusterEffect::Render( void )
{
	ParticleSystem::Render();
}

void ThrusterEffect::initParticle( Particle &out )
{
	if(IsActive)
	{
		D3DXVECTOR3 tempVec;
		out.StartTime = Time;
		out.LifeTime = float(rand()%500)/100;
		out.Position = this->Position;
		if((Throttle > 0))
		{
			tempVec = D3DXVECTOR3( float(rand()%300-150)/100, float(rand()%300-150)/100, -15.0f);
		}
		else if(IsActive && (Throttle < 0))
		{
			tempVec = D3DXVECTOR3( float(rand()%300-150)/100, float(rand()%300-150)/100, 15.0f);
		}
		D3DXVec3TransformCoord(&tempVec,&tempVec, &RotationMatrix);

		out.Velocity = tempVec + SpawnVelocity;
		out.Color = D3DXCOLOR(float(rand()%45)/100, float(rand()%25)/100, 255, 255);
	}
}

void ThrusterEffect::Activate( void )
{
	IsActive = true;
	TimePerParticle = .01f;
}

void ThrusterEffect::Deactivate( void )
{
	IsActive = false;
	TimePerParticle = -1;
}

bool ThrusterEffect::GetIsActive( void )
{
	return IsActive;
}

void ThrusterEffect::SetAngles( D3DXVECTOR3 nAngles)
{
	Angles = nAngles;
}

void ThrusterEffect::SetThrottle( short nThrottle )
{
	Throttle = nThrottle;
}

void ThrusterEffect::SetSpawnVelocity( D3DXVECTOR3 nSpawnVelocity)
{
	SpawnVelocity = nSpawnVelocity;
}